﻿//This filter is tested and seems to work correctly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class Segmentation : BaseImageFilter
    {
        protected int m_rTol = 10;
        protected int m_gTol = 10;
        protected int m_bTol = 10;

        /// <summary>
        /// Initializes a new instance of the <see cref="Segmentation"/> class.
        /// </summary>
        public Segmentation()
            : this(10,10,10)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Segmentation"/> class.
        /// </summary>
        /// <param name="_rTol">The _r tol.</param>
        /// <param name="_gTol">The _g tol.</param>
        /// <param name="_bTol">The _b tol.</param>
        public Segmentation(int _rTol, int _gTol, int _bTol)
        {
            m_rTol = _rTol;
            m_gTol = _gTol;
            m_bTol = _bTol;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Segmentation"/> class.
        /// </summary>
        /// <param name="_segmentation">The _segmentation.</param>
        internal Segmentation(Segmentation _segmentation)
        {
            this.m_rTol = _segmentation.m_rTol;
            this.m_gTol = _segmentation.m_gTol;
            this.m_bTol = _segmentation.m_bTol;
        }

        /// <summary>
        /// Gets or sets the R tolerance.
        /// </summary>
        /// <value>The R tolerance.</value>
        public int RTolerance
        {
            get { return m_rTol; }
            set { m_rTol = value; }
        }

        /// <summary>
        /// Gets or sets the G tolerance.
        /// </summary>
        /// <value>The G tolerance.</value>
        public int GTolerance
        {
            get { return m_gTol; }
            set { m_gTol = value; }
        }

        /// <summary>
        /// Gets or sets the B tolerance.
        /// </summary>
        /// <value>The B tolerance.</value>
        public int BTolerance
        {
            get { return m_bTol; }
            set { m_bTol = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;
            List<Point> aPoint = new List<Point>(){
                new Point(-1,-1), 
                new Point(-1,0),
                new Point(-1,1),
                new Point(0,1),
                new Point(1,0),
                new Point(1,-1),
                new Point(1,1),
                new Point(0,-1)
            };

            List<Point> aPointToSet = new List<Point>();
            UnsafeBitmap bm2 = new UnsafeBitmap(new Bitmap(bitmap.Width, bitmap.Height));
            Color color = Color.Transparent;
            bool bFound = false;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            int i = 0;
            int x = 0;
            int y = 0;
            int j = 0;

            for (y = 1; y < nHeight - 1; y++)
            {
                bFound = false;
                for (x = 1; x < nWidth - 1; x++)
                {
                    color = bitmap.GetPixel(x, y);
                    Color[] aColor = new Color[8];
                    aColor = GetEnvironmentPixelColorArray(bitmap, x, y, aPoint, aColor);
                    aPointToSet.Clear();
                    for (i = 0; i < aColor.Length; i++)
                    {
                        if (ColorFunctions.IsInColorTolerance(color, aColor[i], m_rTol, m_gTol, m_bTol))
                        {
                            int nX = x + aPoint[i].X;
                            int nY = y + aPoint[i].Y;
                            if (bm2.GetPixel(nX, nY) != Color.FromArgb(255, Color.Red))
                            {
                                aPointToSet.Add(new Point(nX, nY));
                                bm2.SetPixel(nX, nY, Color.Red);
                            }
                        }
                    }

                    //TODO: integrate this foreach loop into the one before.
                    for (i = 0; i < aPointToSet.Count; i++)
                    {
                        int nX = aPointToSet[i].X;
                        int nY = aPointToSet[i].Y;
                        aColor = GetEnvironmentPixelColorArray(bitmap, nX, nY, aPoint, aColor);
                        for (j = 0; j < aColor.Length; j++)
                        {
                            if (ColorFunctions.IsInColorTolerance(color, aColor[j], m_rTol, m_gTol, m_bTol))
                            {
                                nX = nX + aPoint[j].X;
                                nY = nY + aPoint[j].Y;
                                if (nX < 0 || nX > nWidth - 1 || nY < 0 || nY > nHeight - 1)
                                    continue;
                                if (bm2.GetPixel(nX, nY) != Color.FromArgb(255, Color.Red))
                                {
                                    aPointToSet.Add(new Point(nX, nY));
                                    bm2.SetPixel(nX, nY, Color.Red);
                                }
                            }
                        }
                    }

                    for (i = 0; i < aPointToSet.Count; i++)
                    {
                        bitmap.SetPixel(aPointToSet[i].X, aPointToSet[i].Y, color);
                    }

                    aPointToSet.Clear();
                }
            }
            bm2.Dispose();
            return _bitmap;
        }

        //TODO: optimize this or use other function in filters
        /// <summary>
        /// Gets the environment pixel color array.
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="aPoint">A point.</param>
        /// <param name="_aColor">Color of the _a.</param>
        /// <returns></returns>
        public static Color[] GetEnvironmentPixelColorArray(UnsafeBitmap bitmap, int x, int y, List<Point> aPoint, Color[] _aColor)
        {
            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            int i = 0;
            int nX = 0;
            int nY = 0;
            for (i = 0; i < aPoint.Count; i++)
            {
                nX = x + aPoint[i].X;
                nY = y + aPoint[i].Y;
                if (nX < 0 || nX > nWidth - 1)
                {
                    continue;
                }
                if (nY < 0 || nY > nHeight - 1)
                {
                    continue;
                }
                _aColor[i] = bitmap.GetPixel(nX, nY);
            }
            return _aColor;
        }

        //TODO: optimize this or use other function in filters
        /// <summary>
        /// Gets the environment pixel color array.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="aPoint">A point.</param>
        /// <param name="_aColor">Color of the _a.</param>
        /// <returns></returns>
        public static Color[] GetEnvironmentPixelColorArray(Color[,] _bitmap, int x, int y, List<Point> aPoint, Color[] _aColor)
        {
            int nWidth = _bitmap.GetLength(0);
            int nHeight = _bitmap.GetLength(1);

            int i = 0;
            int nX = 0;
            int nY = 0;
            for (i = 0; i < aPoint.Count; i++)
            {
                nX = x + aPoint[i].X;
                nY = y + aPoint[i].Y;
                if (nX < 0 || nX > nWidth - 1)
                {
                    continue;
                }
                if (nY < 0 || nY > nHeight - 1)
                {
                    continue;
                }
                _aColor[i] = _bitmap[nX, nY];
            }
            return _aColor;
        }

        //TODO: optimize this or use other function in filters
        /// <summary>
        /// Gets the environment pixel color array.
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="aPoint">A point.</param>
        /// <param name="_aColor">Color of the _a.</param>
        /// <returns></returns>
        public static Color[] GetEnvironmentPixelColorArray(IBitmap bitmap, int x, int y, List<Point> aPoint, Color[] _aColor)
        {
            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            int i = 0;
            for (i = 0; i < aPoint.Count; i++)
            {
                int nX = x + aPoint[i].X;
                int nY = y + aPoint[i].Y;
                if (nX < 0 || nX > nWidth - 1)
                {
                    continue;
                }
                if (nY < 0 || nY > nHeight - 1)
                {
                    continue;
                }
                _aColor[i] = bitmap.GetPixel(nX, nY);
            }
            return _aColor;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Segmentation(this);
        }
    }
}
